// system.cc 
//	Nachos initialization and cleanup routines.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "ctype.h"

// This defines *all* of the global data structures used by Nachos.
// These are all initialized and de-allocated by this file.

Thread *currentThread;			// the thread we are running now
Thread *threadToBeDestroyed;  		// the thread that just finished
Scheduler *scheduler;			// the ready list
Interrupt *interrupt;			// interrupt status
Statistics *stats;			// performance metrics
Timer *timer;				// the hardware timer device,
					// for invoking context switches

int threadTask = 0;			// The int specifying which thread task to run.-Ryan
int memAllocType = 0;
int virtualMemType = -1;
int numPageFaults = 0;
bool printSpecial = FALSE;
#ifdef FILESYS_NEEDED
FileSystem  *fileSystem;
#endif

#ifdef FILESYS
SynchDisk   *synchDisk;
#endif

#ifdef USER_PROGRAM	// requires either FILESYS or FILESYS_STUB
Machine *machine;	// user program memory and registers
InvertedPageTable* IPT;
#endif

#ifdef NETWORK
PostOffice *postOffice;
#endif


// External definition, to allow us to take a pointer to this function
extern void Cleanup();
char* GetType(char c[]);

//----------------------------------------------------------------------
// TimerInterruptHandler
// 	Interrupt handler for the timer device.  The timer device is
//	set up to interrupt the CPU periodically (once every TimerTicks).
//	This routine is called each time there is a timer interrupt,
//	with interrupts disabled.
//
//	Note that instead of calling Yield() directly (which would
//	suspend the interrupt handler, not the interrupted thread
//	which is what we wanted to context switch), we set a flag
//	so that once the interrupt handler is done, it will appear as 
//	if the interrupted thread called Yield at the point it is 
//	was interrupted.
//
//	"dummy" is because every interrupt handler takes one argument,
//		whether it needs it or not.
//----------------------------------------------------------------------
static void
TimerInterruptHandler(int dummy)
{
    if (interrupt->getStatus() != IdleMode)
	interrupt->YieldOnReturn();
}

//----------------------------------------------------------------------
// Initialize
// 	Initialize Nachos global data structures.  Interpret command
//	line arguments in order to determine flags for the initialization.  
// 
//	"argc" is the number of command line arguments (including the name
//		of the command) -- ex: "nachos -d +" -> argc = 3 
//	"argv" is an array of strings, one for each command line argument
//		ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------
void
Initialize(int argc, char **argv)
{
    int argCount;
    char* debugArgs = "";
    bool randomYield = FALSE;

#ifdef USER_PROGRAM

    bool debugUserProg = FALSE;	// single step user program
#endif
#ifdef FILESYS_NEEDED
    bool format = FALSE;	// format disk
#endif
#ifdef NETWORK
    double rely = 1;		// network reliability
    int netname = 0;		// UNIX socket name
#endif
    
    for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
	argCount = 1;
	/* Check the '-A' option and assert for any number of ags greater than 1. If
	   a valid argument is given, the option will be saved in the global variable
	   which can be read by threadtest.cc. */
	if (!strcmp(*argv, "-A"))
	{	    
		//ASSERT(argc > 1);
		if (argc <= 1) 
			printf("Invalid -A input, no argument given\n");
		else {
			if (GetType(*(argv + 1)) == "I")
			{
				memAllocType = atoi(*(argv + 1));
				argCount = 2;	
			}
			else if (GetType(*(argv + 1)) == "C") 
			{
				printf("Invalid -A input, character array given as argument.\n");
				argCount = 2;	
			}
			else if (GetType(*(argv + 1)) == "N")
			{
				printf("Invalid -A input, negative number given as argument.\n");
				argCount = 2;	
			}	
			else if (GetType(*(argv + 1)) == "D")
			{
				printf("Invalid -A input, decimal number given as argument.\n");
				argCount = 2;	
			}
			else if (GetType(*(argv + 1)) == "n")
			{
				printf("Invalid -A input, negative decimal number given as argument.\n");
				argCount = 2;	
			}
		}
	} /* -Ryan */ else if(!strcmp(*argv, "-d")) {
	    if (argc == 1)
		debugArgs = "+";	// turn on all debug flags
	    else {
	    	debugArgs = *(argv + 1);
	    	argCount = 2;
	    }
	} else if (!strcmp(*argv, "-rs")) {
	    ASSERT(argc > 1);
	    RandomInit(atoi(*(argv + 1)));	// initialize pseudo-random
						// number generator
	    randomYield = TRUE;
	    argCount = 2;
	}
	//-------------Andrew----------------
	else if(!strcmp(*argv, "-M")){
		//ASSERT(argc > 1);
		if (argc <= 1) 
			printf("Invalid -M input, no argument given\n");
		else {
			if (GetType(*(argv + 1)) == "I")
			{
				memAllocType = atoi(*(argv + 1));
				argCount = 2;	
			}
			else if (GetType(*(argv + 1)) == "C") 
			{
				printf("Invalid -M input, character array given as argument.\n");
				argCount = 2;	
			}
			else if (GetType(*(argv + 1)) == "N")
			{
				printf("Invalid -M input, negative number given as argument.\n");
				argCount = 2;	
			}	
			else if (GetType(*(argv + 1)) == "D")
			{
				printf("Invalid -M input, decimal number given as argument.\n");
				argCount = 2;	
			}
			else if (GetType(*(argv + 1)) == "n")
			{
				printf("Invalid -M input, negative decimal number given as argument.\n");
				argCount = 2;	
			}
		}
	}//----------------Andrew------------------
	
	//-------------------Ryan----(suck it Andrew, ctrl-c ctrl-v bitch)---------
	else if(!strcmp(*argv, "-V")){
		if (argc <= 1) {
			printf("Invalid -V input, no argument given\n");
			virtualMemType = -1;
		}
		else {
			//ASSERT(argc > 1);
			if (GetType(*(argv + 1)) == "I")
			{
				virtualMemType = atoi(*(argv + 1));
				argCount = 2;
			}
			else if (GetType(*(argv + 1)) == "C")
			{
				printf("Invalid -V input, no argument given\n");
				argCount = 2;
			}
			else if (GetType(*(argv + 1)) == "N")
			{
				printf("Invalid -V input, negative number given as argument\n");
				argCount = 2;
			}
			else if (GetType(*(argv + 1)) == "D")
			{
				printf("Invalid -V input, decimal number given as argument\n");
				argCount = 2;
			}
			else if (GetType(*(argv + 1)) == "n")
			{
				printf("Invalid -V input, negative decimal number given as argument\n");
				argCount = 2;
			}
		}
	}//---------------------------------------------------

	//------------------Andrew----------------(nigga plz, I type with the mouse)----------------
	else if(!strcmp(*argv, "-E"))
		printSpecial = TRUE;

#ifdef USER_PROGRAM
	if (!strcmp(*argv, "-s"))
	    debugUserProg = TRUE;
#endif
#ifdef FILESYS_NEEDED
	if (!strcmp(*argv, "-f"))
	    format = TRUE;
#endif
#ifdef NETWORK
	if (!strcmp(*argv, "-l")) {
	    ASSERT(argc > 1);
	    rely = atof(*(argv + 1));
	    argCount = 2;
	} else if (!strcmp(*argv, "-m")) {
	    ASSERT(argc > 1);
	    netname = atoi(*(argv + 1));
	    argCount = 2;
	}
#endif
    }

    DebugInit(debugArgs);			// initialize DEBUG messages
    stats = new Statistics();			// collect statistics
    interrupt = new Interrupt;			// start up interrupt handling
    scheduler = new Scheduler();		// initialize the ready queue
    if (randomYield)				// start the timer (if needed)
	timer = new Timer(TimerInterruptHandler, 0, randomYield);
	
    threadToBeDestroyed = NULL;

    // We didn't explicitly allocate the current thread we are running in.
    // But if it ever tries to give up the CPU, we better have a Thread
    // object to save its state. 
    currentThread = new Thread("main");		
    currentThread->setStatus(RUNNING);

    interrupt->Enable();
    CallOnUserAbort(Cleanup);			// if user hits ctl-C
    
#ifdef USER_PROGRAM
    machine = new Machine(debugUserProg);	// this must come first
    IPT = new InvertedPageTable();
#endif

#ifdef FILESYS
    synchDisk = new SynchDisk("DISK");
#endif

#ifdef FILESYS_NEEDED
    fileSystem = new FileSystem(format);
#endif

#ifdef NETWORK
    postOffice = new PostOffice(netname, rely, 10);
#endif
}
//---------------------------------------------------------------------
// Integer Identification - Ryan
//	Determines if a string of characters contains only digit 
//	characters and can be classified as an integer. If the string
//	does not contain only digits, returns the the index of the
//	array where the first non digit character is encountered.
//	otherwise returns -1 if only digits are present. -2 is returned
//	when given invalid arguments.
//--------------------------------------------------------------------
int SpanDigits(char c[], int i)
{
	if (i < 0 || i >= strlen(c))
		return -2;
	for (i; i < strlen(c); ++i)
	{
		if (!isdigit(c[i]))
			return i;
	}
	return -1;
}

//----------------------------------------------------------------------
// Type Identification - Ryan and Peter
//	Determines the data type for an array of characters to ensure
//	valid input by the user.
//---------------------------------------------------------------------- 
char* GetType(char c[])
{
	bool isNeg = false;
	int iterator = 0;
	
	if (strlen(c) == 0)
		return "Z";
	if (c[iterator] == '-')
	{
		isNeg = true;
		iterator++;
	}
	iterator = SpanDigits(c, iterator);
	if (iterator != -1)
	{
		if (iterator > 0 && c[iterator] == '.' && SpanDigits(c, (iterator + 1)) == -1 && c[iterator - 1] != '-')
		{
			if (!isNeg)
				return "D";
			else
				return "n";
		}
		else
			return "C";
	}
	else
	{
		if (!isNeg)
			return "I";
		else
			return "N";
	}
}

//---------------------------------------------------------------------
// Input Trim - Peter
//	Removes trailing and leading whitespace characters and returns
//	a character array that has been resized.
//--------------------------------------------------------------------
char* TrimString(char inputString[])
{
	char *start = inputString;
	char *end = inputString + strlen(inputString) - 1;
	
	while(isspace(*start))
		++start;
	while(isspace(*end))
		--end;
	*(end + 1) = '\0';
	char trimmedInput[strlen(start + 1)];
	strcpy(trimmedInput, start);
	char* returnString = new char[sizeof(trimmedInput)];
	strcpy(returnString, trimmedInput);
	return returnString;
}

//----------------------------------------------------------------------
// Cleanup
// 	Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------
void
Cleanup()
{
    printf("\nCleaning up...\n");
#ifdef NETWORK
    delete postOffice;
#endif
    
#ifdef USER_PROGRAM
    delete machine;
#endif

#ifdef FILESYS_NEEDED
    delete fileSystem;
#endif

#ifdef FILESYS
    delete synchDisk;
#endif
    
    delete timer;
    delete scheduler;
    delete interrupt; 

    Exit(0);
}


